<?php

/**
 * 微信支付类
 *
 * @filepath Apps/Common/Service/WechatPayService.class.php
 * @author Zix
 * @version 1.0 , 2014-03-02
 */

namespace Common\Service;

class WechatPayService {

  const SEND_REDPACK = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack';
  
  var $mer_id ;
  var $wx_id ;
  var $appid ;
  var $mch_id ;
  var $send_name ;
  var $ssl_cert ;
  var $ssl_key ;
  var $ca_info ;
  var $ca_path ;
  var $sign_key;
  var $billno ;

  var $error_info = '';
  
  function __construct( $mer_id , $wx_id ) {
    $MerWxSetting = MerWxSettingService::instance();
    $wx_setting_data = $MerWxSetting->get_by_id_merid($wx_id, $mer_id);
    
    $this->mer_id = $mer_id ;
    $this->wx_id  = $wx_id ;
    $this->appid  = $wx_setting_data['app_id'] ;
    $this->mch_id = $wx_setting_data['mch_id'] ;
    $this->send_name = $wx_setting_data['name'] ;
    $this->ssl_cert = getcwd()."/Public/cert/$wx_id/apiclient_cert.pem" ;
    $this->ssl_key  = getcwd()."/Public/cert/$wx_id/apiclient_key.pem" ;
    $this->ca_info  = getcwd()."/Public/cert/$wx_id/rootca.pem" ;
    $this->sign_key = $wx_setting_data['sign_key'] ;
    
  }
    
  /**
   * 发红包
   * @param string $openid 接收方openid
   * @param float $amount 红包金额
   * @param string $wishing 红包祝福语 如：感谢您参加猜灯谜活动，祝您元宵节快乐！
   * @param string $act_name 活动名称 如: 猜灯谜抢红包活动
   * @param string $remark 备注信息 如：猜越多得越多，快来抢！
   */
  function send_redpack( $openid , $amount , $wishing ='感谢参与活动' , $act_name='红包活动' , $remark='快来抢' ) {
    $data = array(
      'nonce_str' => rand_string(16), //随机字符串，不长于32位
      //'sign'       => '', //签名
      'mch_billno' => $this->_gen_billno() , //商户订单号 mch_id+yyyymmdd+10位一天内不能重复的数字。 
      'mch_id' => $this->mch_id, //微信支付分配的商户号
      'wxappid' => $this->appid, //公众账号ID
      'send_name' => $this->send_name, //红包发送者名称 如：天虹百货
      're_openid' => $openid , //用户的openid
      'total_amount' => $amount * 100 , //付款金额，单位分 1000 = 10元
      'total_num' => 1 , //红包发放总人数 如：1
      'wishing' => $wishing , //红包祝福语 如：感谢您参加猜灯谜活动，祝您元宵节快乐！
      //'client_ip' => get_client_ip(0, TRUE), //192.168.0.1
      'client_ip' => '61.184.202.52',
      'act_name' => $act_name, //活动名称 如: 猜灯谜抢红包活动
      'remark' => $remark , //备注信息 如：猜越多得越多，快来抢！
    );
    
    $data['sign'] = $this->_sign($data);
    $xml = $this->_to_xml($data);
    
    $ret = $this->_post_xml_curl( self::SEND_REDPACK , $xml );
    
    if ( !$ret ) {
      //$this->error_info = '发送失败';
      $this->_pay_log($data['mch_billno'] , $openid , $amount , $xml , $ret );
      return FALSE ;
    }
    
    //检查返回结果
    $arr = $this->_from_xml($ret);
    if ( $arr['return_code'] != 'SUCCESS') {
      //出错
      $this->_pay_log($data['mch_billno'] , $openid , $amount , $xml, $ret );
      $this->error_info = $arr['return_msg'];
      return FALSE ;
    }
    
    //检查处理结果
    if( $arr['result_code'] != 'SUCCESS' ) {
      $this->_pay_log($data['mch_billno'] , $openid , $amount , $xml, $ret );
      $this->error_info = $arr['err_code_des'];
      return FALSE ; 
    }
    
    //检查签名
    $sign = $arr['sign'];
    unset($arr['sign']);
    if ( $this->_sign($arr) != $sign ) {
      $this->_pay_log($data['mch_billno'] , $openid , $amount , $xml, $ret );
      $this->error_info = '返回签名错误';
      return FALSE ;
    }
    
    $this->_pay_log($data['mch_billno'] , $openid , $amount , $xml, $ret , $arr['send_listid'] );
    return TRUE ;
  }
  
  /**
   * 生成订单号
   * @return string
   */
  private function _gen_billno() {
    //生成 支付订单号 mch_id+yyyymmdd+10位一天内不能重复的数字。 
    return $this->mch_id . date('Ymd') . mt_rand(10000 , 99999) . mt_rand(10000 , 99999)  ;
  }
    
  /**
   * 数据签名
   * 
   * @param array $data
   * @return string
   */
  private function _sign($data) {
    ksort($data);
    $arr = array();
    foreach ($data as $key => $val) {
      $arr[] = "$key=$val";
    }

    $str_tmp = implode('&', $arr) . '&key=' . $this->sign_key ;
    return strtoupper(md5($str_tmp));
  }

  /**
   * 数组 转 xml
   * 
   * @param type $data
   * @return string
   */
  private function _to_xml($data) {
    $xml = "<xml>\r\n";
    foreach ($data as $key => $val) {
      if (is_numeric($val)) {
        $xml .= "<" . $key . ">" . $val . "</" . $key . ">\r\n";
      } else {
        $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">\r\n";
      }
    }
    $xml.="</xml>\r\n";
    return $xml;
  }
  
  /**
   * xml 转 数组
   * 
   * @param type $xml
   * @return boolean
   */
  function _from_xml( $xml ) {
		if(!$xml){
			return FALSE ;
		}
    libxml_disable_entity_loader(true);
    return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), TRUE );		
  }

  /**
   * 以post方式提交xml到对应的接口url
   * 
   * @param string $url  url
   * @param string $xml  需要post的xml数据
   * @param bool $use_cert 是否需要证书，默认不需要
   * @param int $second   url执行超时时间，默认30s
   * @throws WxPayException
   */
  private function _post_xml_curl( $url , $xml , $second = 30) {
    
    echo $this->ssl_cert . '<br>' . $this->ssl_key .'<br>' . $this->ca_info.'<br>' ;
    
    $ch = curl_init();
    curl_setopt($ch , CURLOPT_URL        , $url    );
    curl_setopt($ch , CURLOPT_POSTFIELDS , $xml    );
    curl_setopt($ch , CURLOPT_TIMEOUT    , $second ); //设置超时
    
    curl_setopt($ch , CURLOPT_POST           , TRUE );
    curl_setopt($ch , CURLOPT_RETURNTRANSFER , TRUE );
    
//		curl_setopt($ch , CURLOPT_SSL_VERIFYPEER , TRUE );
//		curl_setopt($ch , CURLOPT_SSL_VERIFYHOST , 2    );//严格校验
    
		curl_setopt($ch , CURLOPT_SSL_VERIFYPEER , FALSE );
		curl_setopt($ch , CURLOPT_SSL_VERIFYHOST , FALSE );

    curl_setopt($ch , CURLOPT_SSLCERT , $this->ssl_cert );
    //curl_setopt($ch , CURLOPT_SSLCERTPASSWD , $this->mch_id );
    curl_setopt($ch , CURLOPT_SSLKEY  , $this->ssl_key );
    //curl_setopt($ch , CURLOPT_CAPATH  , $this->ca_path );
    curl_setopt($ch , CURLOPT_CAINFO  , $this->ca_info );

    //运行curl
    $data = curl_exec($ch);
    
    //返回结果
    if ($data) {
      curl_close($ch);
      return $data;
    } else {
      //curl_error($ch)
      $error = curl_error($ch);
      curl_close($ch);
      //echo $error ;
      $this->error_info = "curl出错，错误码:$error" ;
      return FALSE ;
    }
  }
  
  /**
   * 记录wx_pay表
   * 
   * @param string $billno
   * @param string $openid
   * @param float $amount
   * @param string $send_data
   * @param string $response_data
   */
  function _pay_log( $billno , $openid , $amount , $send_data , $response_data , $send_listid ) {
    $data = array(
      'mer_id' => $this->mer_id ,
      'wx_id' => $this->wx_id ,
      'type' => 'redpack' ,
      'billno' => $billno ,
      'openid' => $openid ,
      'amount' => $amount * 100 ,
      'send_data' => $send_data ,
      'response_data' => $response_data ,
      'send_listid' => $send_listid ,
    );
    $WxPay = M('WxPay');
    $WxPay->add( $data );
  }

}
